//
//  RPAlertController.m
//  RPAlertControllerDemo
//
//  Created by iOS on 21/11/22.
//  Copyright (c) 2021年 sword. All rights reserved.
//

#import <Masonry/Masonry.h>
#import "RPAlertController.h"
#import "RPAlertTransitioningHandler.h"
/**
 * 弹出视图控制器
 */
@interface RPAlertController ()
// 单击手势
@property (nonatomic, weak) UITapGestureRecognizer *singleTap;

// 拖曳时需要修改的约束
@property (nonatomic, weak) MASConstraint *draggingCons;
// 弹出容器
@property (nonatomic, strong) UIView *alertView;
// 背景
@property (nonatomic, strong) UIView *backgroundView;
// 转场动画的处理类
@property (nonatomic, strong) RPAlertTransitioningHandler *transitioningHandler;

@end

@implementation RPAlertController
{
    CGPoint beganPoint;
}

#pragma mark - Public

+ (instancetype)alertControllerWithAlertView:(UIView *)alertView style:(RPAlertControllerStyle)style animation:(RPAlertTransitionAnimation)animation {
    RPAlertController *alertVC = [[RPAlertController alloc] init];

    alertVC->_alertView = alertView;
    alertVC->_preferredStyle = style;
    alertVC->_transitionAnimation = animation;
    alertVC.transitioningHandler.transitionAnimation = animation;
    return alertVC;
}

- (void)setBackgoundTapDismissEnable:(BOOL)backgoundTapDismissEnable {
    _backgoundTapDismissEnable = backgoundTapDismissEnable;

    if (_singleTap) {
        _singleTap.enabled = backgoundTapDismissEnable;
    }
}

- (void)setDraggingDismissEnable:(BOOL)draggingDismissEnable {
    _draggingDismissEnable = draggingDismissEnable;

    if (_draggingPan) {
        _draggingPan.enabled = draggingDismissEnable;
    }
}

#pragma mark - Life cycle

- (instancetype)init {
    if (self = [super init]) {
        self.providesPresentationContextTransitionStyle = YES;
        self.definesPresentationContext = YES;
        self.modalPresentationStyle = UIModalPresentationCustom;
        
        self.transitioningHandler = [[RPAlertTransitioningHandler alloc] init];
        self.transitioningDelegate = self.transitioningHandler;
        
        _draggingThreshold = 10.0;
        _preferredStyle = RPAlertControllerStyleAlert;
        _transitioningHandler.transitionAnimation = RPAlertTransitionAnimationNormal;
        _backgoundTapDismissEnable = YES;
        _alertViewCenterYOffset = 0;
        _alertStyleEdging = 15;
        _actionSheetStyleEdging = 0;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor clearColor];

    // 添加背景
    [self.view insertSubview:self.backgroundView atIndex:0];
    [self.backgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(UIEdgeInsetsZero);
    }];

    // 添加单击手势
    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
    singleTap.enabled = _backgoundTapDismissEnable;
    self.backgroundView.userInteractionEnabled = YES;
    [self.backgroundView addGestureRecognizer:singleTap];
    self.singleTap = singleTap;

    // 添加拖曳手势
    if (self.preferredStyle != RPAlertControllerStyleAlert) {
        UIPanGestureRecognizer *draggingPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(draggingPan:)];
        draggingPan.maximumNumberOfTouches = 1;
        draggingPan.enabled = _draggingDismissEnable;
        [self.view addGestureRecognizer:draggingPan];
        self.draggingPan = draggingPan;
    }

    // 添加alertView
    [self.view addSubview:self.alertView];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self layoutConstraints];
    });
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:NO];
}

- (void)dealloc {
    NSLog(@"%s", __func__);
}

#pragma mark - Action

- (void)singleTap:(UITapGestureRecognizer *)sender {
    if(self.dismissBlock){
        self.dismissBlock();
    }
    [self.view endEditing:YES];
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)draggingPan:(UIPanGestureRecognizer *)sender {
    CGPoint point = [sender locationInView:self.view];

    NSLog(@"%s x=%lf,y=%lf state=%ld", __func__, point.x, point.y, sender.state);
    switch (sender.state) {
        case UIGestureRecognizerStateBegan: { /*记录手势开开始点的位置*/
            beganPoint = point;
            break;
        }
        case UIGestureRecognizerStateChanged: { /*更新alertView的约束*/
            CGPoint offset = CGPointMake(point.x - beganPoint.x, point.y - beganPoint.y);
            [self moveAlertViewWithOffset:offset];
            beganPoint = point;
            break;
        }
        case UIGestureRecognizerStateEnded: { /*销毁当前VC或还原当前AlertView的位置*/
            [self moveAlertViewEnded];
            break;
        }
        default: {
            break;
        }
    }
   [self.view endEditing:YES];
}

#pragma mark - Private
#pragma mark - 拖曳手势处理

- (void)moveAlertViewWithOffset:(CGPoint)offset {
    NSLayoutConstraint *constraint = [self.draggingCons valueForKey:@"layoutConstraint"];

    switch (self.preferredStyle) {
        case RPAlertControllerStyleActionSheet: {
            constraint.constant = constraint.constant + offset.y;

            // 限制向上移动,导致alertView脱离底部
            if (constraint.constant < 0) {
                constraint.constant = 0;
            }
            break;
        }
        case RPAlertControllerStyleSideSlipLeft: {
            constraint.constant = constraint.constant + offset.x;

            // 限制向右移动,导致alertView脱离左边
            if (constraint.constant > 0) {
                constraint.constant = 0;
            }
            break;
        }
        case RPAlertControllerStyleSideSlipRight: {
            constraint.constant = constraint.constant + offset.x;

            // 限制向左移动,导致alertView脱离右边
            if (constraint.constant < 0) {
                constraint.constant = 0;
            }
            break;
        }
        default:
            break;
    }
}

- (void)moveAlertViewEnded {
    if (self.preferredStyle == RPAlertControllerStyleAlert) {
        return;
    }
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];

    // 以alertView的长宽做为参考,当移动距离超出一半时,需要销毁当前VC
    NSLayoutConstraint *constraint = [self.draggingCons valueForKey:@"layoutConstraint"];
    CGFloat moveDistance = fabsf(constraint.constant);
    CGFloat height = self.alertView.frame.size.height;
    CGFloat width = self.alertView.frame.size.width;
    CGFloat dismissDistance = 0.f;

    if (self.preferredStyle == RPAlertControllerStyleActionSheet) {
        dismissDistance = height / _draggingThreshold;
    } else {
        dismissDistance = width / 2.0;
    }
    BOOL shouldDissmiss = NO;

    if (moveDistance > 0 && dismissDistance > 0 && moveDistance > dismissDistance) {
        shouldDissmiss = YES;
    }

    // 计算手势释放时,alertView移动的位置toValue, toValue=0表示还原
    CGFloat toValue = shouldDissmiss ? dismissDistance * _draggingThreshold : 0.f;

    if (toValue > 0 && self.preferredStyle == RPAlertControllerStyleSideSlipLeft) {
        toValue = -toValue;
    }
    NSLog(@"1-%s toValue=%lf,shouldDissmiss=%d,self.alertView.frame=%@", __func__, toValue, shouldDissmiss, NSStringFromCGRect(self.alertView.frame));

    // 更新约束,并添加动画
    self.draggingCons.equalTo(@(toValue));
    [UIView animateWithDuration:0.25 animations:^{
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        NSLog(@"2-%s finished=%d,toValue=%lf,shouldDissmiss=%d,self.alertView.frame=%@", __func__, finished, toValue, shouldDissmiss, NSStringFromCGRect(self.alertView.frame));

        if (shouldDissmiss) {
            if(self.dismissBlock){
                self.dismissBlock();
            }
            [self.backgroundView removeFromSuperview];
            [self dismissViewControllerAnimated:NO completion:nil];
        }
    }];
}

#pragma mark - 布局

// 根据style对alertView进行布局
- (void)layoutConstraints {
    switch (_preferredStyle) {
        case RPAlertControllerStyleActionSheet:
            [self layoutActionSheetStyleView];
            break;
        case RPAlertControllerStyleAlert:
            [self layoutAlertStyleView];
            break;
        case RPAlertControllerStyleSideSlipLeft:
        case RPAlertControllerStyleSideSlipRight:
            [self layoutSideSlipView];
            break;
        default:
            break;
    }
    [self.view layoutIfNeeded];
}

- (void)layoutAlertStyleView {
    [self.alertView layoutIfNeeded];
    CGFloat maxYOffset = (CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.alertView.bounds)) / 2.0;

    if (_alertViewCenterYOffset > maxYOffset) {
        _alertViewCenterYOffset = maxYOffset;
    }

    if (_alertViewCenterYOffset < -maxYOffset) {
        _alertViewCenterYOffset = -maxYOffset;
    }
    // centerX,Width
    CGFloat maxAlertW = CGRectGetWidth(self.view.bounds) - 2 * _alertStyleEdging;

    if (!CGSizeEqualToSize(self.alertView.bounds.size, CGSizeZero)) {
        CGFloat alertW = CGRectGetWidth(self.alertView.bounds);
        alertW = (alertW > maxAlertW) ? maxAlertW : alertW;
        [self.alertView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(@(alertW));
            make.height.equalTo(@(CGRectGetHeight(self.alertView.bounds)));
            make.centerY.equalTo(@(_alertViewCenterYOffset));
            make.centerX.equalTo(@0);
        }];
    } else {
        BOOL findAlertViewWidthConstraint = NO;

        for (NSLayoutConstraint *constraint in self.alertView.constraints) {
            if (constraint.firstAttribute == NSLayoutAttributeWidth) {
                findAlertViewWidthConstraint = YES;
                break;
            }
        }

        if (!findAlertViewWidthConstraint) {
            [self.alertView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.width.equalTo(@(maxAlertW));
                make.height.equalTo(self.alertView.mas_width);
                make.centerY.equalTo(@(_alertViewCenterYOffset));
                make.centerX.equalTo(@0);
            }];
        }
    }
}

- (void)layoutActionSheetStyleView {
    // remove width constaint
    for (NSLayoutConstraint *constraint in self.alertView.constraints) {
        if (constraint.firstAttribute == NSLayoutAttributeWidth) {
            [self.alertView removeConstraint:constraint];
            break;
        }
    }
    // add edge and height constraint
    CGFloat alertH = CGRectGetHeight(self.alertView.bounds);
    alertH = (alertH > 0) ? alertH : 100;
    [self.alertView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(@(_actionSheetStyleEdging));
        make.right.equalTo(@(-_actionSheetStyleEdging));
        make.height.equalTo(@(alertH));
        self.draggingCons = make.bottom.equalTo(@0);
    }];
}

- (void)layoutSideSlipView {
    if ((_preferredStyle != RPAlertControllerStyleSideSlipLeft) && (_preferredStyle != RPAlertControllerStyleSideSlipRight)) {
        return;
    }

    for (NSLayoutConstraint *constraint in self.alertView.constraints) {
        if (constraint.firstAttribute == NSLayoutAttributeWidth) {
            [self.alertView removeConstraint:constraint];
            break;
        }
    }
    CGFloat alertW = CGRectGetWidth(self.alertView.bounds);

    if (alertW <= 0 || alertW >= CGRectGetWidth(self.view.bounds)) {
        alertW = CGRectGetWidth(self.view.bounds) * 4 / 5.0;
    }
    [self.alertView mas_makeConstraints:^(MASConstraintMaker *make) {
        if (_preferredStyle == RPAlertControllerStyleSideSlipLeft) {
            self.draggingCons = make.left.equalTo(@0);
        } else if (_preferredStyle == RPAlertControllerStyleSideSlipRight) {
            self.draggingCons = make.right.equalTo(@0);
        }
        make.top.bottom.equalTo(@0);
        make.width.equalTo(@(alertW));
    }];
}

#pragma mark - Getters

- (UIView *)backgroundView {
    if (!_backgroundView) {
        _backgroundView = [[UIView alloc] init];
        _backgroundView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3];
    }
    return _backgroundView;
}

- (UIView *)alertView {
    if (!_alertView) {
        _alertView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
        _alertView.backgroundColor = UIColor.whiteColor;
    }
    return _alertView;
}

#pragma mark - RPAlertDelegate

- (UIViewController *)alertController {
    return self;
}

@end

